
/*    This file is part of the managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.managarm.jmanacclib.frontend.korona;

// import util classes
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.managarm.jmanacclib.core.CclibUnit;
import org.managarm.jmanacclib.core.CclibException;
import org.managarm.jmanacclib.generic.GnMethod;
import org.managarm.jmanacclib.generic.GnProcedure;
import org.managarm.jmanacclib.generic.GnMthCompiler;
import org.managarm.jmanacclib.generic.GnFrame;
import org.managarm.jmanacclib.generic.GnInst;

//import managarm virtual architecture
import org.managarm.jmanavalib.core.VaClass;
import org.managarm.jmanavalib.core.VaClsInfo;
import org.managarm.jmanavalib.core.VaDatatype;
import org.managarm.jmanavalib.core.VaFldInfo;
import org.managarm.jmanavalib.core.VaMthInfo;
import org.managarm.jmanavalib.core.VaProcedure;
import org.managarm.jmanavalib.core.VaLiteral;
import org.managarm.jmanavalib.utils.VaImage;

import ast.AstArray;
import ast.AstArrayAccess;
import ast.AstAssignment;
import ast.AstBlock;
import ast.AstCall;
import ast.AstCast;
import ast.AstClass;
import ast.AstCondDo;
import ast.AstCondFor;
import ast.AstCondIf;
import ast.AstCondWhile;
import ast.AstConstBoolean;
import ast.AstConstChar;
import ast.AstConstInteger;
import ast.AstConstNull;
import ast.AstConstString;
import ast.AstDelegateOf;
import ast.AstGeneric;
import ast.AstIncrement;
import ast.AstInstance;
import ast.AstNew;
import ast.AstNoAction;
import ast.AstOperation;
import ast.AstReturn;
import ast.AstRoot;
import ast.AstSizeOf;
import ast.AstSpace;
import ast.AstSymbol;
import ast.AstTerm;
import ast.AstVariable;
import ast.AstXNatAddrCast;
import ast.AstXNatDlgMethod;
import ast.AstXNatDlgObject;
import ast.AstXNatMthAddr;
import ast.AstXNatObjSize;
import ast.AstXNatAddressOf;
import ast.AstXNatMakeDelegate;
import ast.AstXNatRefOperation;
import ast.AstXAtomCas;
import ast.AstXPerfArrayCopy;

public class KorMthCompiler {
	// describes a variable
	private static final class Register {
		public boolean used;
	}

	// register zero
	private static final int REGISTER_NULL = 0;
	
	// type compatibility constants
	private static final int CO_EQUAL = 0;
	private static final int CO_IMPLICIT = 1;
	private static final int CO_EXPLICIT = 2;
	private static final int CO_INVALID = 3;
	
	// action constants
	// return the expression type
	private static final int ACTION_RESOLVE = 1; 
	// load an expression into a register
	private static final int ACTION_LOAD = 2;
	// store the register in a field
	private static final int ACTION_STORE = 3;
	// invoke a method (ignore it's return value)
	private static final int ACTION_INVOKE = 4; 
	// object allocation
	private static final int ACTION_ALLOC = 5;
	// return the return type of an allocation
	private static final int ACTION_ALLOC_RESOLVE = 7; // return the return
	// resolves addressof
	private static final int ACTION_XNAT_MTHADDR = 8;
	// resolves delegates
	private static final int ACTION_DELEGATE = 9;
	 // return the delegate return type
	private static final int ACTION_DELEGATE_RESOLVE = 10;

	// the ast class / method we try to compile
	private final KorAstNode astMethod;
	private final AstClass astClass;
	
	// the root of our ast
	private final AstRoot astRoot;
	
	// the image that contains our classes
	private final VaImage image;
	
	// description of this method
	private final VaMthInfo methodInfo;
	
	// file that contains our method
	private final CclibUnit unit;
	
	// register that contains the this pointer
	private int thisRegister;

	private int currentLabel = 0;

	// variable name to register map
	private final HashMap<String, Integer> variables
			= new HashMap<String, Integer>();

	// register state
	private final ArrayList<Register> registers
			= new ArrayList<Register>();
	
	// class and image compilers that are used to compile this method
	private final KorCompiler imageCmp;
	private final KorClsCompiler classCmp;
	
	// generic method object that stores the method description
	private final GnMethod methodObject;
	// generic code object that stores the intermediate code of this method
	private GnProcedure codeObject;
	
	// reference to the frame object of the current frame
	private GnFrame currentFrame;
	
	// constructor for the method compiler
	public KorMthCompiler(KorCompiler imgcmp, KorClsCompiler clscmp,
			AstRoot root, AstClass cls, KorAstNode method, VaImage image,
			VaMthInfo mthinfo) {
		this.astRoot = root;
		this.astClass = cls;
		this.astMethod = method;
		this.unit = (CclibUnit)cls.getAttribute("unit");
		this.image = image;
		this.methodInfo = mthinfo;
		this.imageCmp = imgcmp;
		this.classCmp = clscmp;
		
		int realargcnt = mthinfo.getArgumentCnt();
		if((mthinfo.flags0 & VaMthInfo.FLAG0_STATIC) == 0)
			realargcnt++;
		this.methodObject = new GnMethod(realargcnt);
		this.codeObject = methodObject.codeObject;
	}

	// compiles the method
	public final VaProcedure build() throws CclibException {
		// create a new generic code frame
		currentFrame = createFrame();
		
		// create registers for the arguments
		for(int i = 0; i < astMethod.getAttrInt("NumberOfArguments"); i++){
			localMap(astMethod.getAttrStr("ArgumentName" + i),
					classCmp.typeResolve(astMethod.getChild("ArgumentType" + i)));
		}
		
		// create a register for the this object
		if((methodInfo.flags0 & VaMthInfo.FLAG0_STATIC) == 0) {
			thisRegister = localMap("this",
					new VaDatatype.DtClass(classCmp.getClsInfo()));
		}
		
		// compile the method to generic code
		buildStatement(astMethod.getChild("Statement"));
		putInst(new GnInst.InstReturn(0));
				
		// compile the method to intermediate code
		final GnMthCompiler gncompiler = new GnMthCompiler(methodObject);
		final VaProcedure procedure = gncompiler.compile();
		if(methodInfo.ident.equals("initialize"))
			System.nanoTime();
		return procedure;
	}

	private final GnFrame createFrame() {
		GnFrame frame = new GnFrame();
		codeObject.frames.add(frame);
		return frame;
	}

	private final void putInst(final GnInst opcode) {
		if(opcode instanceof GnInst.InstCast) {
			final GnInst.InstCast inst = (GnInst.InstCast)opcode;
			if(registerType(inst.source) instanceof VaDatatype.DtClass
					&& inst.datatype instanceof VaDatatype.DtPrimitive) {
				System.out.println("CastViolation: In method "
						+ astMethod.getAttrStr("MethodName"));
			}
		}
		currentFrame.instructions.add(opcode);
	}

	// map a variable to a register
	private final int localMap(final String name, final VaDatatype type) {
		// allocate a register for the local
		final int register = registerAlloc(type);
		// map the variable name to the register
		variables.put(name, register);
		return register;
	}

	private final void localUnmap(final String name) {
		// unmap the variable name
		Integer reg = variables.remove(name);
		// mark the register a unused
		registers.get(reg.intValue() - 1).used = false;
	}

	private final int localLookup(final String name) {
		// lookup the variable
		final Integer reg = variables.get(name);
		if(reg == null)
			return 0;
		// return the register
		return reg.intValue();
	}

	private final int localLookup(final KorAstNode node) {
		// check whether the node is a term
		if(node instanceof AstTerm == false)
			return 0;
		final AstTerm term = (AstTerm) node;
		// check whether the node has childs
		if(term.getChild() != null)
			return 0;
		return localLookup(term.getIdentifier());
	}

	private final int registerAlloc(final VaDatatype type) {
		// loop through the registers and try to find a suitable one
		/*final int nextreg = registers.size() + 1;
		for(int i = 1; i < nextreg; i++){
			// get the variable state
			final Register register = registers.get(i - 1);
			if(register.used == false){
				// get the variable type
				final VaDatatype reg = methodObject.registers.get(i - 1);
				if(type.compatible(reg)){
					register.used = true;
					return i;
				}
			}
		}*/

		// add it to the registers list and to the code object
		Register register = new Register();
		register.used = true;
		registers.add(register);
		methodObject.registers.add(type);
		
		return registers.size();
	}

	private final VaDatatype registerType(final int register) {
		return methodObject.registers.get(register - 1);
	}

	private final void registerFree(final int register) {
		registers.get(register - 1).used = false;
	}

	/*
	 * returns an integer representing the format of a primitive type (floating
	 * point number, integer, char, boolean etc.)
	 */
	private final int primitiveFormat(final int primitive) throws CclibException {
		switch(primitive) {
		case VaDatatype.DtPrimitive.PRIM_BYTE:
		case VaDatatype.DtPrimitive.PRIM_UBYTE:
		case VaDatatype.DtPrimitive.PRIM_SHORT:
		case VaDatatype.DtPrimitive.PRIM_USHORT:
		case VaDatatype.DtPrimitive.PRIM_INT:
		case VaDatatype.DtPrimitive.PRIM_UINT:
		case VaDatatype.DtPrimitive.PRIM_LONG:
		case VaDatatype.DtPrimitive.PRIM_ULONG:
			return 1;
		case VaDatatype.DtPrimitive.PRIM_CHAR:
			return 2;
		case VaDatatype.DtPrimitive.PRIM_BOOLEAN:
			return 3;
		}
		throw new CclibException("Internal error: Unknown primitive " + primitive
				+ " for primitiveFormat()", unit, -1);
	}

	/* returns an integer representing the size/index of a primitive type */
	private final int primitiveIndex(final int primitive) throws CclibException {
		switch(primitive) {
		case VaDatatype.DtPrimitive.PRIM_CHAR:
			return 0;
		case VaDatatype.DtPrimitive.PRIM_BOOLEAN:
			return 0;
		case VaDatatype.DtPrimitive.PRIM_BYTE:
			return 1;
		case VaDatatype.DtPrimitive.PRIM_UBYTE:
			return 2;
		case VaDatatype.DtPrimitive.PRIM_SHORT:
			return 3;
		case VaDatatype.DtPrimitive.PRIM_USHORT:
			return 4;
		case VaDatatype.DtPrimitive.PRIM_INT:
			return 5;
		case VaDatatype.DtPrimitive.PRIM_UINT:
			return 6;
		case VaDatatype.DtPrimitive.PRIM_LONG:
			return 7;
		case VaDatatype.DtPrimitive.PRIM_ULONG:
			return 8;
		}
		throw new CclibException("Internal error: Unknown primitive " + primitive
				+ " for primitiveIndex()", unit, -1);
	}

	/* returns 0 if prim1 is compatible to prim2 */
	private final int primitiveCompatible(final int prim1, final int prim2)
			throws CclibException {
		// compare the formats of the primitives
		if(primitiveFormat(prim1) != primitiveFormat(prim2))
			return CO_INVALID;
		final int index1 = primitiveIndex(prim1);
		final int index2 = primitiveIndex(prim2);
		// compare the indices of the primitives
		if(index1 == index2)
			return CO_EQUAL;
		if(index1 < index2)
			return CO_IMPLICIT;
		return CO_INVALID;
	}

	/* returns 0 if type1 is compatible to type2 */
	private final int typesCompatible(final VaDatatype type1,
			final VaDatatype type2) throws CclibException {
		return typesCompatible(type1, type2, false);
	}
	private final int typesCompatible(final VaDatatype type1,
			final VaDatatype type2, boolean pass_to_method) throws CclibException {
		// type1 == VOID ??
		if(type1 instanceof VaDatatype.DtPrimitive) {
			final VaDatatype.DtPrimitive prim = (VaDatatype.DtPrimitive)type1;
			
			if(prim.identity == VaDatatype.DtPrimitive.PRIM_NULL)
				return CO_IMPLICIT;
		}
		
		if(type1 instanceof VaDatatype.DtPrimitive && type2 instanceof VaDatatype.DtPrimitive){
			final VaDatatype.DtPrimitive prim1 = (VaDatatype.DtPrimitive) type1;
			final VaDatatype.DtPrimitive prim2 = (VaDatatype.DtPrimitive) type2;
			// compare the primitives
			return primitiveCompatible(prim1.identity, prim2.identity);
		}else if(type1 instanceof VaDatatype.DtClass && type2 instanceof VaDatatype.DtClass){
			final VaDatatype.DtClass cls1 = (VaDatatype.DtClass) type1;
			final VaDatatype.DtClass cls2 = (VaDatatype.DtClass) type2;
			
			// they must both be struct or not sturct
			// FIXME:
//			if(((cls1.flags0 & VaDatatype.DtClass.FLAG0_STRUCT) != 0)
//					!= ((cls2.getFlags0() & VaDatatype.DtClass.FLAG0_STRUCT) != 0))
//				return CO_INVALID;
			
			VaClass cls = imageCmp.lookupClass(cls1.clsInfo)
					.getClassBody();
			while(cls != null){
				// compare the classes
				if(cls.clsInfo.compatible(cls2.clsInfo))
					return CO_EQUAL;
				// go on with the parent of cls1
				if(cls.parentInfo == null){
					cls = null;
				}else{
					cls = imageCmp.lookupClass(cls.parentInfo).getClassBody();
				}
			}
		}else if(type1 instanceof VaDatatype.DtArray && type2 instanceof VaDatatype.DtArray){
			final VaDatatype.DtArray array1 = (VaDatatype.DtArray) type1;
			final VaDatatype.DtArray array2 = (VaDatatype.DtArray) type2;
			if((array1.flags0 & VaDatatype.DtArray.FLAG0_XNAT_REFCOUNTED) != 0
					&& (array2.flags0 & VaDatatype.DtArray.FLAG0_XNAT_REFCOUNTED) == 0
					&& (array2.flags0 & VaDatatype.DtArray.FLAG0_LOCAL) == 0)
				return CO_INVALID;
			if((array1.flags0 & VaDatatype.DtArray.FLAG0_LOCAL) != 0
					&& (array2.flags0 & VaDatatype.DtArray.FLAG0_LOCAL) == 0)
				return CO_INVALID;
			if((array2.flags0 & VaDatatype.DtArray.FLAG0_LOCAL) != 0
					&& pass_to_method == false)
				return CO_INVALID;
			if(array1.basetype.compatible(array2.basetype))
				return CO_EQUAL;
		}else if(type1 instanceof VaDatatype.DtDelegate && type2 instanceof VaDatatype.DtDelegate) {
			final VaDatatype.DtDelegate delegate1 = (VaDatatype.DtDelegate)type1;
			final VaDatatype.DtDelegate delegate2 = (VaDatatype.DtDelegate)type2;
			
			if(delegate1.compatible(delegate2))
				return CO_EQUAL;
		}
		
		return CO_INVALID;
	}

	// returns an array that contains the types of the arguments of call
	private final VaDatatype[] resolveArguments(final AstCall call)
			throws CclibException {
		final int count = call.parameterCount();
		final VaDatatype[] args = new VaDatatype[count];
		for(int i = 0; i < count; i++)
			args[i] = resolve(call.parameterAt(i));
		return args;
	}

	// returns 0 if the method is compatible to ident and args
	private final int mthIsSuitable(VaMthInfo mth, String ident,
			VaDatatype[] args) throws CclibException {
		// compare the identifiers
		if(mth.ident.equals(ident) == false)
			return -1;
		// compare the argument count
		if(mth.getArgumentCnt() != args.length)
			return -1;
		// get the arg count
		final int argcnt = mth.getArgumentCnt();
		// loop through the args of the method
		for(int i = 0; i < argcnt; i++){
			// compare the argument types
			if(typesCompatible(args[i],
					mth.getArgument(i), true) > CO_IMPLICIT){
				return -1;
			}
		}
		// the types are compatible without casts
		return 0;
	}

	// finds a specific method in a class
	private final VaMthInfo findSuitableMth(VaClass cls, String ident,
			VaDatatype[] args, AstCall call) throws CclibException {
		// get the method count
		final int mthcnt = cls.getMethodCnt();
		// loop through the methods
		for(int i = 0; i < mthcnt; i++){
			// get the next method
			final VaMthInfo mth = cls.getMethod(i);
			if(mthIsSuitable(mth, ident, args) == 0){
				return mth;
			}
		}
		
		if(cls.parentInfo == null)
			return null;
		return findSuitableMth(imageCmp.lookupClass(cls.parentInfo)
				.getClassBody(), ident, args, call);
	}

	// compiles a block of statements
	private final void buildBlock(final AstBlock block) throws CclibException {
		// first pass: compile the child statements
		Iterator<KorAstNode> iter = block.childIter();
		while(iter.hasNext()){
			// fetch the next statement
			final KorAstNode child = iter.next();
			// is it a variable declaration?
			if(child instanceof AstVariable){
				AstVariable stat = (AstVariable) child;
				// resolve the variable type
				final VaDatatype vartype = classCmp.typeResolve(stat.getType());

				// map the variable identifier to a register
				if(localLookup(stat.getIdentifier()) != 0)
					throw new CclibException("Compile error: Duplicate local variable "
							+ stat.getIdentifier(), unit, stat.line());
				final int register = localMap(stat.getIdentifier(), vartype);
				
				if(stat.getExpression() != null) {
					final KorAstNode exp = stat.getExpression();
					
					VaDatatype exptype = resolve(exp);
					
					// load the expression into "expreg"
					final int exptempreg = registerAlloc(exptype);
					load(exp, exptempreg);
					int expreg = exptempreg;
					if(suitableForOperation(vartype, exptype) == false) {
						final VaDatatype optype = suitableTypeForOperation(vartype,
								exptype, child.line());
						expreg = registerAlloc(optype);
						putInst(new GnInst.InstCast(optype, expreg, exptempreg));
						registerFree(exptempreg);
					}
					
					putInst(new GnInst.InstClone(register, expreg));
					
					registerFree(expreg);
				}
			}else{
				// process the statement
				buildStatement(child);
			}
		}

		// second pass: free variables that are used by the block
		iter = block.childIter();
		while(iter.hasNext()){
			// fetch the next statement
			final KorAstNode child = iter.next();
			// is it a variable declaration?
			if(child instanceof AstVariable){
				final AstVariable variable = (AstVariable) child;
				// unmap the variable
				localUnmap(variable.getIdentifier());
			}
		}
	}

	// compiles a statement
	private final void buildStatement(final KorAstNode node) throws CclibException {
		if(node instanceof AstBlock){
			buildBlock((AstBlock) node);
		}else if(node instanceof AstNoAction){
			// ignore this statement
		}else if(node instanceof AstTerm){
			compile(ACTION_INVOKE, node, REGISTER_NULL, null);
		}else if(node instanceof AstReturn) {
			final AstReturn ret = (AstReturn) node;
			if(ret.getExpression() == null){
				putInst(new GnInst.InstReturn(0));
			}else{
				// load the returned value into a register
				final int expreg = operandCast(methodInfo.returnType,
						ret.getExpression());
				
				// generate the "return" instruction
				putInst(new GnInst.InstReturn(expreg));
				registerFree(expreg);
			}
		}else if(node instanceof AstAssignment){
			final AstAssignment st = (AstAssignment) node;
			
			// get the field and expression nodes
			final KorAstNode exp = st.getExpression();
			final KorAstNode fld = st.getField();

			VaDatatype fldtype = resolve(fld);
			VaDatatype exptype = resolve(exp);
			
			if(typesCompatible(exptype, fldtype) > CO_IMPLICIT) {
				throw new CclibException("Compile error: Operand type mismatch", unit, st.line());
			}
			
			// load the field
			boolean fldfree = false;
			int fldreg = localLookup(fld);
			if(fldreg == REGISTER_NULL){
				// allocate a register for the fld
				fldreg = registerAlloc(fldtype);
				
				// load the field
				if(st.getOperator() != AstAssignment.OP_ASSIGN)
					load(fld, fldreg);
				
				// set the free flag
				fldfree = true;
			}
			
			// load the expression into "expreg"
			final int exptempreg = registerAlloc(exptype);
			load(exp, exptempreg);
			int expreg = exptempreg;
			if(suitableForOperation(fldtype, exptype) == false) {
				final VaDatatype optype = suitableTypeForOperation(fldtype,
						exptype, node.line());
				expreg = registerAlloc(optype);
				putInst(new GnInst.InstCast(optype, expreg, exptempreg));
				registerFree(exptempreg);
			}
			
			switch(st.getOperator()) {
			case AstAssignment.OP_ASSIGN:
				putInst(new GnInst.InstClone(fldreg, expreg));
				break;
			case AstAssignment.OP_ADD_ASSIGN:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_ADD,
								fldreg, fldreg, expreg));
				break;
			case AstAssignment.OP_SUB_ASSIGN:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_SUB,
								fldreg, fldreg, expreg));
				break;
			case AstAssignment.OP_MUL_ASSIGN:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_MUL,
								fldreg, fldreg, expreg));
				break;
			case AstAssignment.OP_DIV_ASSIGN:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_DIV,
								fldreg, fldreg, expreg));
				break;
			case AstAssignment.OP_OR_ASSIGN:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_OR,
								fldreg, fldreg, expreg));
				break;
			case AstAssignment.OP_AND_ASSIGN:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_AND,
								fldreg, fldreg, expreg));
				break;
			default:
				throw new CclibException("Internal error: " +
						"Illegal assignment operator", unit, node.line());
			}
			
			// free allocated registers
			registerFree(expreg);
			if(fldfree){
				// store the result in the field
				store(fld, fldreg);
				
				// free the field register
				registerFree(fldreg);
			}
		}else if(node instanceof AstCondIf){
			final AstCondIf branch = (AstCondIf) node;
			
			// count chained else ifs
			int count = 0;
			KorAstNode current = branch;
			while(current != null
					&& current instanceof AstCondIf) {
				final AstCondIf ifstatement = (AstCondIf)current;
				count++;
				current = ifstatement.getElseStatement();
			}
			KorAstNode tailelse = current;
			
			// reserve frame numbers for the chained elses
			final GnFrame[] frames = new GnFrame[count];
			for(int i = 0; i < count; i++) {
				currentLabel++;
				frames[i] = createFrame();
			}
			final int baselabel = currentLabel;
			currentLabel++;
			final int nextlabel = currentLabel;
			final GnFrame nextframe = createFrame();
			
			// compile the conditions
			int index = 0;
			current = branch;
			while(current != null
					&& current instanceof AstCondIf) {
				final AstCondIf ifstatement = (AstCondIf)current;
				compileJumpIfTrue(ifstatement.getExpression(), baselabel - index);
				index++;
				current = ifstatement.getElseStatement();
			}
			
			// compile the last/tail else statement
			if(tailelse != null)
				buildStatement(tailelse);
			putInst(new GnInst.InstJump(nextlabel, 0));
			
			// compile the statements
			index = 0;
			current = branch;
			while(current != null
					&& current instanceof AstCondIf) {
				final AstCondIf ifstatement = (AstCondIf)current;
				index++;
				currentFrame = frames[count - index];
				buildStatement(ifstatement.getCaseStatement());
				putInst(new GnInst.InstJump(nextlabel, 0));
				current = ifstatement.getElseStatement();
			}
			
			currentFrame = nextframe;
		}else if(node instanceof AstCondFor){
			AstCondFor branch = (AstCondFor) node;
			// create a frame for the loop body
			// and a frame for code after the end of the loop
			currentLabel++;
			final int looplabel = currentLabel;
			final GnFrame loopframe = createFrame();
			currentLabel++;
			final int endlabel = currentLabel;
			final GnFrame endframe = createFrame();
			
			// process the loop body
			putInst(new GnInst.InstJump(looplabel, 0));
			currentFrame = loopframe;
			compileJumpIfFalse(branch.getExpression(), endlabel);
			buildStatement(branch.getBlockStatement());
			buildStatement(branch.getLoopStatement());
			putInst(new GnInst.InstJump(looplabel, 0));
			currentFrame = endframe;
		}else if(node instanceof AstCondWhile){
			// get the for statement node
			AstCondWhile branch = (AstCondWhile) node;
			
			// create a frame for the loop body
			// and a frame for following code
			currentLabel++;
			final int looplabel = currentLabel;
			final GnFrame loopframe = createFrame();
			currentLabel++;
			final int nextlabel = currentLabel;
			final GnFrame nextframe = createFrame();
			
			putInst(new GnInst.InstJump(looplabel, 0));
			currentFrame = loopframe;
			compileJumpIfFalse(branch.getExpression(), nextlabel);
			buildStatement(branch.getStatement());
			putInst(new GnInst.InstJump(looplabel, 0));
			currentFrame = nextframe;
		}else if(node instanceof AstCondDo){
			/*// get the for statement node
			AstCondDo branch = (AstCondDo) node;
			// get a label id for the loop label
			currentLabel++;
			int looplabel = currentLabel;
			// add a Label inst for the loop label
			putLabel(looplabel);
			// process the loop statement
			buildStatement(branch.getStatement());
			// process the branch
			compileJumpIfFalse(branch.getExpression(), looplabel);*/
			System.out.println("FIXME: do loop");
		}else if(node instanceof AstIncrement){
			final AstIncrement inc = (AstIncrement) node;
			
			// load the field into a variable
			final KorAstNode field = inc.getField();
			final VaDatatype fieldtype = resolve(field);
			boolean free = false;
			int register = localLookup(field);
			if(register == 0) {
				register = registerAlloc(fieldtype);
				load(field, register);
				free = true;
			}
			
			final VaDatatype temptype = new VaDatatype.DtPrimitive
				(VaDatatype.DtPrimitive.PRIM_UINT);
			final int tempreg = registerAlloc(temptype);
			putInst(new GnInst.InstLoad(tempreg, new VaLiteral.LitUInt(1)));
			int expreg = tempreg;
			if(typesCompatible(temptype, fieldtype) != CO_EQUAL) {
				expreg = registerAlloc(fieldtype);
				putInst(new GnInst.InstCast(fieldtype, expreg, tempreg));
				registerFree(tempreg);
			}
			
			switch(inc.getOperator()) {
			case AstIncrement.OP_INCREMENT:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_ADD,
								register, register, expreg));
				break;
			case AstIncrement.OP_DECREMENT:
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_SUB,
								register, register, expreg));
				break;
			default:
				throw new CclibException("Internal error: " +
						"Invalid increment operator", unit, node.line());
			}
			// free the register and store the value
			registerFree(expreg);
			if(free){
				store(field, register);
				registerFree(register);
			}
		}else if(node.attrStrEquals("Name", "TryCatch")) {
			
			final GnProcedure tryblock = new GnProcedure();
			final GnProcedure thisblock = codeObject;
			
			final int curlabel = currentLabel;
			final GnFrame curframe = currentFrame;
			
			// compile the try-block XXX: remove this hack here
			codeObject = tryblock;
			currentLabel = 0;
			currentFrame = createFrame();
			buildStatement(node.getChild("Statement"));
			putInst(new GnInst.InstLeave());
			codeObject = thisblock;
			currentFrame = curframe;
			currentLabel = curlabel;

			currentLabel++;
			final int finallabel = currentLabel;
			final GnFrame finalframe = createFrame();
			currentLabel++;
			final int catchlabel = currentLabel;
			final GnFrame catchframe = createFrame();
			
			final int exception = registerAlloc(new VaDatatype.DtClass
					(new VaClsInfo("Korona.Exception", 0, 0, 0, 0)));
			catchframe.exception = exception;
			
			final int catchnum = node.getAttrInt("NumberOfCatches");
			if(catchnum == 0) {
				throw new CclibException("Compile error: try without catch",
						unit, node.line());
			}
			
			// insert the catch instruction
			putInst(new GnInst.InstCatch(exception, catchlabel, tryblock));

			final GnFrame[] catchframes = new GnFrame[catchnum];

			// compile the precondition exception != null
			currentFrame = catchframe;
			final int condreg = registerAlloc(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_BOOLEAN));
			final int nullreg = registerAlloc(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_NULL));
			putInst(new GnInst.InstLoad(nullreg, new VaLiteral.LitNull()));
			putInst(new GnInst.InstBinOperation
					(GnInst.InstBinOperation.OP_SETIF_EQUALS,
							condreg, exception, nullreg));
			putInst(new GnInst.InstJump(finallabel, condreg));
			
			// compile the instanceof switches
			for(int i = 0; i < catchnum; i++) {
				currentLabel++;
				final int caseframe = currentLabel;
				catchframes[i] = createFrame();
				
				final VaDatatype datatype = classCmp.typeResolve
						(node.getChild("CatchType" + i));
				putInst(new GnInst.InstInstanceof(datatype, exception, condreg));
				putInst(new GnInst.InstJump(caseframe, condreg));
			}
			putInst(new GnInst.InstThrow(exception));
			registerFree(nullreg);
			registerFree(condreg);
			
			// compile the catch-blocks bodies
			for(int i = 0; i < catchnum; i++) {
				currentFrame = catchframes[i];
				
				final String variable = node.getAttrStr("CatchVariable" + i);
				if(localLookup(variable) != 0)
					throw new CclibException("Compile error: Duplicate local variable "
							+ variable, unit, node.line());
				final VaDatatype datatype = classCmp.typeResolve
						(node.getChild("CatchType" + i));
				
				final int dest = localMap(variable, datatype);
				putInst(new GnInst.InstCast(datatype, dest, exception));
				buildStatement(node.getChild("CatchStatement" + i));
				localUnmap(variable);
				putInst(new GnInst.InstJump(finallabel, 0));
			}
			currentFrame = finalframe;
			registerFree(exception);
		}else if(node.attrStrEquals("Name", "Throw")) {
			final KorAstNode exception = node.getChild("Exception");
			
			final VaDatatype datatype = resolve(exception);
			final int register = registerAlloc(datatype);
			load(exception, register);
			putInst(new GnInst.InstThrow(register));
			registerFree(register);
		}else if(node instanceof AstXNatRefOperation) {
			final AstXNatRefOperation statement = (AstXNatRefOperation) node;
			
			final int register = registerAlloc(resolve(statement.operand));
			load(statement.operand, register);
			if(statement.operation == AstXNatRefOperation.REF_INC) {
				putInst(new GnInst.InstXNatRefOperation
						(GnInst.InstXNatRefOperation.REF_INCREMENT, register));
			}else{
				putInst(new GnInst.InstXNatRefOperation
						(GnInst.InstXNatRefOperation.REF_DECREMENT, register));
			}
			registerFree(register);
		}else if(node instanceof AstXPerfArrayCopy){
			final AstXPerfArrayCopy statement = (AstXPerfArrayCopy) node;
			
			final int dest = registerAlloc(resolve(statement.dest));
			final int source = registerAlloc(resolve(statement.source));
			load(statement.dest, dest);
			load(statement.source, source);
			final int destoff = operandCast(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_UINT), statement.destOffset);
			final int srcoff = operandCast(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_UINT), statement.sourceOffset);
			final int size = operandCast(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_UINT), statement.size);
			putInst(new GnInst.InstXPerfArrayCopy(dest,
					source, destoff, srcoff, size));
			registerFree(dest);
			registerFree(source);
			registerFree(destoff);
			registerFree(srcoff);
			registerFree(size);
		}else{
			throw new CclibException("Compile error: Unknown statement "
					+ node.getClass().getSimpleName(), unit, node.line());
		}
	}

	
	// wrappers for compile with action = ACTION_RESOLVE
	private final VaDatatype resolve(final KorAstNode node)
			throws CclibException {
		return resolve(node, null);
	}
	private final VaDatatype resolve(final KorAstNode node,
			VaDatatype restype) throws CclibException {
		return compile(ACTION_RESOLVE, node, REGISTER_NULL, restype);
	}

	// wrappers for compile with action = ACTION_LOAD
	private final VaDatatype load(final KorAstNode node, int register)
			throws CclibException {
		return load(node, register, null);
	}
	private final VaDatatype load(final KorAstNode node, int register,
			VaDatatype restype) throws CclibException {
		return compile(ACTION_LOAD, node, register, restype);
	}

	// wrappers for compile with action = ACTION_STORE
	public final VaDatatype store(final KorAstNode node, int register)
			throws CclibException {
		return store(node, register, null);
	}
	private final VaDatatype store(final KorAstNode node, int register,
			VaDatatype restype) throws CclibException {
		return compile(ACTION_STORE, node, register, restype);
	}

	// compiles an expression
	private final VaDatatype compile(int action, KorAstNode node,
			int result, VaDatatype restype) throws CclibException {
		if(node instanceof AstTerm){
			final AstTerm term = (AstTerm) node;
			final String ident = term.getIdentifier();
			final AstSymbol next = term.getChild();
			
			// lookup the local variable
			final int reg = localLookup(ident);
			if(reg != REGISTER_NULL){
				// get the register type
				final VaDatatype type = registerType(reg);
				
				if(next == null) {
					if(action == ACTION_LOAD){
						putInst(new GnInst.InstClone(result, reg));
					}else if(action == ACTION_STORE) {
						putInst(new GnInst.InstClone(reg, result));
					}else if(action != ACTION_RESOLVE){
						throw new CclibException("Compile error: " +
								"Invalid action for local variable",
								unit, node.line());
					}
					// return the register's type
					return type;
				}else{
					return compileMemberSymbol(action, type, reg, next, result);
				}
			}
			
			// is it a primitive type?
			final VaDatatype prim = KorUtils.primResolve(ident);
			if(prim != null){
				// compile sub symbols
				if(term.getChild() != null)
					return compileStaticSymbol(action, prim, term.getChild(),
							result);
				
				if(action != ACTION_RESOLVE)
					throw new CclibException("Compile error: " +
							"Invalid action for type", unit, term.line());

				// return the primitive type
				return prim;
			}
			
			// is it a generic pattern?
			final VaDatatype insttype = classCmp.getPattern(ident);
			if(insttype != null){
				// compile sub symbols
				if(term.getChild() != null)
					return compileStaticSymbol(action, insttype, term
							.getChild(), result);
				if(action != ACTION_RESOLVE)
					throw new CclibException("Compile error: " +
							"Invalid action for type", unit, term.line());
				// return the pattern type
				return insttype;
			}
			
			// it may be part of the ast root
			try{
				return compileStatic(action, astRoot, node, result);
			}catch (KorUtils.UnresolvedException e) { }
			
			// it may be part of the current class
			try {
				return compileClassMember(action, classCmp.getClassBody(),
						node, result);
			}catch(KorUtils.UnresolvedException e){ }
			
			// it may be part of the current object
			try {
				return compileObjectMember(action, classCmp.getClassBody(),
						thisRegister, node, result);
			}catch(KorUtils.UnresolvedException e) { }
			
			// it may be part of the current namespace
			try{
				return compileStatic(action, astClass.getContainer(), node,
						result);
			}catch (KorUtils.UnresolvedException e){
			}
			
			// it may be part of a namespace that is imported from our class
			final Iterator<AstTerm> iter = astClass.importIter();
			while(iter.hasNext()){
				final AstTerm include = iter.next();
				
				final AstSpace space = KorUtils.spaceResolve(include, astClass,
						astRoot);
				if(space == null)
					throw new CclibException("Compile error: "
							+ "Could not resolve space " + symbolToString(include),
							unit, node.line());
				try {
					return compileStatic(action, space, node, result);
				} catch (KorUtils.UnresolvedException e) { }
			}
			throw new CclibException("Compile error: Could not resolve "
					+ symbolToString(term), unit, node.line());
		}else if(node instanceof AstConstInteger){
			final AstConstInteger literal = (AstConstInteger) node;
			final VaDatatype type;
			final long value = literal.getValue();
			
			// determine the integer type/length
			// FIXME: soon!
			if(value < (1 << 7)){
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_BYTE);
			}else if(value < (1 << 8)){
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UBYTE);
			}else if(value < (1 << 15)){
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_SHORT);
			}else if(value < (1 << 16)){
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_USHORT);
			}else if(value < (1L << 31)){
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_INT);
			}else if(value < (1L << 32)){
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT);
			}else if(value < (1L << 63)){
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_LONG);
			}else{
				type = new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_ULONG);
			}
			
			if(action == ACTION_LOAD){
				putInst(new GnInst.InstLoad(result,
						new VaLiteral.LitUInt(literal.getValue())));
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Internal error: Invalid action " + action
						+ " for AstConstInteger", unit, node.line());
			}
			
			// return the literal's type
			return type;
		}else if(node instanceof AstConstChar) {
			final AstConstChar literal = (AstConstChar) node;
			if(action == ACTION_LOAD){
				putInst(new GnInst.InstLoad(result,
						new VaLiteral.LitChar(literal.getValue())));
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Internal error: Invalid action " + action
						+ " for char literal", unit, node.line());
			}
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_CHAR);
		}else if(node instanceof AstConstString){
			// get the literal node
			final AstConstString literal = (AstConstString) node;
			if(action == ACTION_LOAD){
				putInst(new GnInst.InstLoad(result, new VaLiteral.LitCharArray
						(literal.getValue().toCharArray())));
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Internal error: Invalid action " + action
						+ " for string literal", unit, node.line());
			}
			return new VaDatatype.DtArray(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_CHAR), 0);
		}else if(node instanceof AstConstBoolean){
			// get the literal node
			final AstConstBoolean literal = (AstConstBoolean) node;
			if(action == ACTION_LOAD){
				putInst(new GnInst.InstLoad(result, new VaLiteral.LitBoolean
						(literal.getValue())));
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Compile error: Invalid action " + action
						+ " for boolean literal", unit, node.line());
			}
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_BOOLEAN);
		}else if(node instanceof AstConstNull){
			if(action == ACTION_LOAD){
				// add the load inst
				putInst(new GnInst.InstLoad(result, new VaLiteral.LitNull()));
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Compile error: Invalid action " + action
						+ " for null literal", unit, node.line());
			}
			
			//if(restype == null)
			//	System.err.println("No resulting type specified");
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_NULL);
		}else if(node.attrStrEquals("Name", "NegateNumber")) {
			final KorAstNode operand = node.getChild("Operand");
			
			if(action == ACTION_LOAD){
				final int reg = operandLoad(operand);
				putInst(new GnInst.InstUnOperation
						(GnInst.InstUnOperation.OP_ARITH_NEG, result, reg));
				registerFree(reg);
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Compile error: Invalid action",
						unit, node.line());
			}
			
			return resolve(operand);
		}else if(node.attrStrEquals("Name", "NegateBits")) {
			final KorAstNode operand = node.getChild("Operand");
			
			if(action == ACTION_LOAD){
				final int reg = operandLoad(operand);
				putInst(new GnInst.InstUnOperation
						(GnInst.InstUnOperation.OP_ARITH_NOT, result, reg));
				registerFree(reg);
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Compile error: Invalid action",
						unit, node.line());
			}
			
			return resolve(operand);
		}else if(node instanceof AstOperation){
			// compile the operation
			return compileOperation(action, (AstOperation) node, result);
		}else if(node instanceof AstCast){
			final AstCast cast = (AstCast) node;
			
			// resolve the dest type
			final VaDatatype type = classCmp.typeResolve(cast.getSymbol());
			
			if(action == ACTION_LOAD){
				// load the operand
				boolean free = false;
				int reg = localLookup(cast.getExpression());
				if(reg == REGISTER_NULL){
					final VaDatatype srctype = resolve(cast.getExpression());
					reg = registerAlloc(srctype);
					load(cast.getExpression(), reg);
					free = true;
				}
				
				putInst(new GnInst.InstCast(type, result, reg));
				
				if(free)
					registerFree(reg);
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Compile error: Invalid action",
						unit, node.line());
			}
			// return the target type
			return type;
		}else if(node instanceof AstNew) {
			final AstNew op = (AstNew) node;
			
			if(action == ACTION_RESOLVE){
				return compile(ACTION_ALLOC_RESOLVE, op.getConstructor(),
						REGISTER_NULL, null);
			}else if(action == ACTION_LOAD){
				return compile(ACTION_ALLOC, op.getConstructor(), result, null);
			}else{
				throw new CclibException("Compile error: " +
						"Invalid action for object creation",
						unit, node.line());
			}
		}else if(node.attrStrEquals("Name", "NewArray")) {
			final VaDatatype datatype
					= classCmp.typeResolve(node.getChild("Datatype"));
			
			if(action == ACTION_LOAD) {
				final int register = registerAlloc(new VaDatatype.DtPrimitive
						(VaDatatype.DtPrimitive.PRIM_UINT));
				load(node.getChild("Length"), register);
				putInst(new GnInst.InstCreateArray(datatype, register, result));
				registerFree(register);
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Compile error: " +
						"Invalid action for new() operation",
						unit, node.line());
			}
			return datatype;
		}else if(node instanceof AstSizeOf){
			final AstSizeOf op = (AstSizeOf) node;
			if(action == ACTION_LOAD){
				// load the field into a virtual register
				boolean free = false;
				int reg = localLookup(op.getSymbol());
				if(reg == REGISTER_NULL){
					final VaDatatype type = resolve(op.getSymbol());
					reg = registerAlloc(type);
					load(op.getSymbol(), reg);
					free = true;
				}
				
				putInst(new GnInst.InstUnOperation
						(GnInst.InstUnOperation.OP_ARR_SIZE, result, reg));
						
				// free allocated registers
				if(free)
					registerFree(reg);
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Internal error: Invalid action " + action
						+ " for sizeof()", unit, node.line());
			}
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT);
		}else if(node instanceof AstDelegateOf) {
			final AstDelegateOf op = (AstDelegateOf)node;
			if(action == ACTION_LOAD) {
				compile(ACTION_DELEGATE, op.getSymbol(), result, null);
				return null;
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for delegateof", unit, node.line());
			}
			return compile(ACTION_DELEGATE_RESOLVE, op.getSymbol(), REGISTER_NULL, null);
		}else if(node.attrStrEquals("Name", "ArrayOf")) {
			// figure out the array's type & basetype
			final VaDatatype basetype;
			final VaDatatype.DtArray arraytype;
			if(node.hasAttribute("Datatype")) {
				arraytype = (VaDatatype.DtArray)classCmp.typeResolve
						(node.getChild("Datatype"));
				basetype = arraytype.basetype;
			}else{
				basetype = new VaDatatype.DtClass
						(new VaClsInfo("Korona.Object", 0, 0, 0, 0));
				arraytype = new VaDatatype.DtArray(basetype, 0);
			}
			final VaDatatype indextype = new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_UINT);
			
			if(action == ACTION_LOAD) {
				final int indexreg = registerAlloc(indextype);
				final int basereg = registerAlloc(basetype);
				
				final int elementnum = node.getAttrInt("NumberOfElements");
				
				putInst(new GnInst.InstLoad(indexreg, new VaLiteral.LitUInt
						(elementnum)));
				putInst(new GnInst.InstCreateArray(arraytype,
						indexreg, result));
				
				for(int i = 0; i < elementnum; i++) {
					putInst(new GnInst.InstLoad(indexreg,
							new VaLiteral.LitUInt(i)));
					load(node.getChild("Element" + i), basereg);
					putInst(new GnInst.InstArrAccess
							(GnInst.InstArrAccess.OP_STORE,
							result, indexreg, basereg));
				}
				
				return null;
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for arrayof", unit, node.line());
			}
			
			return arraytype;
		}else if(node instanceof AstXNatAddrCast) {
			// get the ast node
			final AstXNatAddrCast cast = (AstXNatAddrCast)node;
			
			// resolve the type
			final VaDatatype casttype = classCmp.typeResolve(cast.getType());
			
			if(action == ACTION_LOAD){
				boolean free = false;

				// load the address into a register
				int reg = localLookup(cast.getAddress());
				if(reg == REGISTER_NULL){
					// resolve the symbol's type
					final VaDatatype type = resolve(cast.getAddress());
					
					// allocate a register for the address
					reg = registerAlloc(type);
					
					// load the address into it
					load(cast.getAddress(), reg);
					
					// set the free flag
					free = true;
				}

				putInst(new GnInst.InstXNatAddressCast(result, reg, casttype));
				
				// free allocated registers
				if(free)
					registerFree(reg);
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for _xnat_addrcast", unit, node.line());
			}
			
			// return the type
			return casttype;
		}else if(node instanceof AstXNatObjSize) {
			// get the ast node
			final AstXNatObjSize cast = (AstXNatObjSize)node;
			
			// resolve the type
			final VaDatatype casttype = classCmp.typeResolve(cast.getType());
			
			if(action == ACTION_LOAD){
				putInst(new GnInst.InstXNatSizeofObject(result, casttype));
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for _xnat_objsize", unit, node.line());
			}
			
			// return the type
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT);
		}else if(node instanceof AstXNatMthAddr) {
			final AstXNatMthAddr op = (AstXNatMthAddr)node;
			
			if(action == ACTION_LOAD){
				compile(ACTION_XNAT_MTHADDR, op.getSymbol(), result, null);
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for _xnat_mthaddr", unit, node.line());
			}
			
			// return the type
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT);
		}else if(node instanceof AstXNatAddressOf) {
			final AstXNatAddressOf builtin = (AstXNatAddressOf)node;
			
			if(action == ACTION_LOAD) {
				// load the object into a register
				final int objectreg = registerAlloc(resolve(builtin.getObject()));
				load(builtin.getObject(), objectreg);
				
				// generate the "address of" instruction
				putInst(new GnInst.InstXNatAddressOf(GnInst.InstXNatAddressOf.ADDRESS_OBJECT,
						objectreg, result));
			}else if(action == ACTION_RESOLVE) {
				return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT);
			}else {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for _xnat:addressof", unit, node.line());
			}
			return null;
		}else if(node instanceof AstXNatDlgMethod) {
			final AstXNatDlgMethod op = (AstXNatDlgMethod)node;
			
			if(action == ACTION_LOAD) {
				int register = localLookup(op.getDelegate());
				if(register == REGISTER_NULL) {
					register = registerAlloc(resolve(op.getDelegate()));
					load(op.getDelegate(), register);
					putInst(new GnInst.InstXNatAddressOf
							(GnInst.InstXNatAddressOf.ADDRESS_DLG_METHOD, register, result));
					registerFree(register);
				}else{
					putInst(new GnInst.InstXNatAddressOf
							(GnInst.InstXNatAddressOf.ADDRESS_DLG_METHOD, register, result));
				}
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for _xunsafe_address_cast", unit, node.line());
			}
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT);
		}else if(node instanceof AstXNatDlgObject) {
			final AstXNatDlgObject op = (AstXNatDlgObject)node;
			
			if(action == ACTION_LOAD) {
				int register = localLookup(op.getDelegate());
				if(register == REGISTER_NULL) {
					register = registerAlloc(resolve(op.getDelegate()));
					load(op.getDelegate(), register);
					putInst(new GnInst.InstXNatAddressOf
							(GnInst.InstXNatAddressOf.ADDRESS_DLG_OBJECT, register, result));
					registerFree(register);
				}else{
					putInst(new GnInst.InstXNatAddressOf
							(GnInst.InstXNatAddressOf.ADDRESS_DLG_OBJECT, register, result));
				}
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for _xunsafe_address_cast", unit, node.line());
			}
			return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_UINT);
		}else if(node instanceof AstXNatMakeDelegate) {
			final AstXNatMakeDelegate op = (AstXNatMakeDelegate)node;
			
			final VaDatatype datatype = classCmp.typeResolve(op.type);
			if(action == ACTION_LOAD) {
				final int method = registerAlloc(resolve(op.method));
				final int object = registerAlloc(resolve(op.object));
				load(op.method, method);
				load(op.object, object);
				putInst(new GnInst.InstXNatMakeDelegate(datatype, method, object, result));
				registerFree(method);
				registerFree(object);
			}else if(action != ACTION_RESOLVE) {
				throw new CclibException("Internal error: Invalid action " + action
						+ " for _nat:mk_delegate", unit, node.line());
			}
			
			return datatype;
		}
		
		throw new CclibException("Internal error: Unknown ast node "
				+ node.getClass().getSimpleName(), unit, node.line());
	}

	private final VaDatatype compileOperation(int action, AstOperation operation,
			int result) throws CclibException {
		final KorAstNode operand1 = operation.getOperand1();
		final KorAstNode operand2 = operation.getOperand2();
		
		if(operation.operator() == AstOperation.OP_SPECIAL_INSTANCEOF) {
			final VaDatatype type1 = resolve(operand1);
			
			if(action == ACTION_LOAD) {
				// load the first operand
				boolean free1 = false;
				int register1 = localLookup(operand1);
				if(register1 == REGISTER_NULL){
					register1 = registerAlloc(type1);
					load(operand1, register1);
					free1 = true;
				}
				
				// resolve the second operand
				final VaDatatype type = classCmp.typeResolve(operand2);
				
				putInst(new GnInst.InstInstanceof(type, register1, result));
				
				// free the operand registers
				if(free1)
					registerFree(register1);
			}else if(action == ACTION_RESOLVE) {
				return new VaDatatype.DtPrimitive(VaDatatype.DtPrimitive.PRIM_BOOLEAN);
			}else{
				throw new CclibException("Compile error: Invalid action for instanceof",
						unit, operation.line());
			}
			
			return null;
		}
		
		//	 resolve the operand types
		final VaDatatype type1 = resolve(operand1);
		final VaDatatype type2 = resolve(operand2);
		final VaDatatype.DtPrimitive prim1 = (VaDatatype.DtPrimitive) type1;
		final VaDatatype.DtPrimitive prim2 = (VaDatatype.DtPrimitive) type2;
		
		// check for type mismatches
		if(primitiveFormat(prim1.identity) != primitiveFormat(prim2.identity)){
			throw new CclibException("Compile error: Incompatible primitive types",
					unit, operation.line());
		}
		
		// determine the primitive indices
		final int index1 = primitiveIndex(prim1.identity);
		final int index2 = primitiveIndex(prim2.identity);
		
		// determine the resulting type
		final VaDatatype.DtPrimitive resulttype = (index1 > index2) ? prim1 : prim2;

		if(action == ACTION_LOAD){
			// load the operands
			final int tempreg1 = registerAlloc(type1);
			final int tempreg2 = registerAlloc(type2);
			load(operand1, tempreg1);
			load(operand2, tempreg2);
			int register1 = tempreg1;
			int register2 = tempreg2;
			if(typesCompatible(type1, resulttype) != CO_EQUAL) {
				register1 = registerAlloc(resulttype);
				putInst(new GnInst.InstCast(resulttype, register1, tempreg1));
				registerFree(tempreg1);
			}
			if(typesCompatible(type2, resulttype) != CO_EQUAL) {
				register2 = registerAlloc(resulttype);
				putInst(new GnInst.InstCast(resulttype, register2, tempreg2));
				registerFree(tempreg2);
			}
			
			final int op = operation.operator();
			if(op == AstOperation.OP_ARITHMETIC_ADD){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_ADD,
						result, register1, register2));
			}else if(op == AstOperation.OP_ARITHMETIC_SUB){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_SUB,
						result, register1, register2));
			}else if(op == AstOperation.OP_ARITHMETIC_MUL){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_MUL,
						result, register1, register2));
			}else if(op == AstOperation.OP_ARITHMETIC_DIV){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_DIV,
						result, register1, register2));
			}else if(op == AstOperation.OP_ARITHMETIC_MOD){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_MOD,
						result, register1, register2));
			}else if(op == AstOperation.OP_BINARY_AND){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_AND,
						result, register1, register2));
			}else if(op == AstOperation.OP_BINARY_OR){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_OR,
						result, register1, register2));
			}else if(op == AstOperation.OP_BINARY_XOR){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_XOR,
						result, register1, register2));
			}else if(op == AstOperation.OP_BINARY_SHL){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_SHL,
						result, register1, register2));
			}else if(op == AstOperation.OP_BINARY_SHR){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_SHR,
						result, register1, register2));
			}else if(op == AstOperation.OP_COMPARE_EQUALS){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_SETIF_EQUALS,
						result, register1, register2));
			}else if(op == AstOperation.OP_COMPARE_GREATER){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_SETIF_ABOVE,
						result, register1, register2));
			}else if(op == AstOperation.OP_COMPARE_LESS){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_SETIF_BELOW,
						result, register1, register2));
			}else if(op == AstOperation.OP_LOGICAL_AND){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_AND,
						result, register1, register2));
			}else if(op == AstOperation.OP_LOGICAL_OR){
				putInst(new GnInst.InstBinOperation
						(GnInst.InstBinOperation.OP_ARITH_OR,
						result, register1, register2));
			}else{
				throw new CclibException("Internal error: Unknown operator " + op,
						unit, operation.line());
			}
			
			// free the operands
			registerFree(register1);
			registerFree(register2);
		}else if(action != ACTION_RESOLVE){
			throw new CclibException("Compile error: Invalid action for operation",
					unit, operation.line());
		}

		return resulttype;
	}

	// compiles non-static field load/store operations
	// and non-static method calls
	// see compileObjectElement for more details
	private final VaDatatype compileObjectMember(final int action,
			final VaClass cls, final int object,
			final KorAstNode node, final int result) throws CclibException,
			KorUtils.UnresolvedException {
		if(node instanceof AstTerm){
			final AstTerm term = (AstTerm) node;
			final String ident = term.getIdentifier();
			
			// lookup the method or field
			VaClass clsbody = cls;
			while(clsbody != null) {
				final int fldcnt = clsbody.getFieldCnt();
				for(int i = 0; i < fldcnt; i++){
					final VaFldInfo fld = clsbody.getField(i);
					if(fld.ident.equals(ident) == false)
						continue;
					return compileField(action, fld, object, term.getChild(),
							result);
				}
				
				final int mthcnt = clsbody.getMethodCnt();
				for(int i = 0; i < mthcnt; i++){
					final VaMthInfo mth = clsbody.getMethod(i);
					if(!mth.ident.equals(ident))
						continue;
					if(term.getChild() == null)
						throw new CclibException("Compile error: " + ident
								+ " is a method", unit, term.line());
					return compileCall(action, new VaDatatype.DtClass(clsbody.clsInfo),
							object, ident, (AstCall)term.getChild(), result);
				}
				
				clsbody = clsbody.parentInfo == null
						? null : imageCmp.lookupClass(clsbody.parentInfo).getClassBody();
			}
			
			throw new KorUtils.UnresolvedException();
		}else{
			throw new CclibException("Compile error: Invalid subsymbol "
					+ node.getClass().getSimpleName(), unit, node.line());
		}
	}

	/*
	 * compiles non-static field load/store operations and non-static method
	 * calls. calls compileObjectMember internally but catches the unresolvable
	 * exception. call this method if the object must contain the element. call
	 * compileObjectMember to find out whether an object contains a specific
	 * element e.g. call compileObjectMember to resolve references to fields of
	 * the current object
	 */
	private final VaDatatype compileObjectElement(int action, VaClass cls,
			int object, AstSymbol symbol, int result) throws CclibException {
		try{
			return compileObjectMember(action, cls, object, symbol, result);
		}catch (KorUtils.UnresolvedException e) {
			throw new CclibException("Compile error: Could not find "
					+ symbolToString(symbol) + " in an object of class "
					+ cls.clsInfo.ident, unit, symbol.line());
		}
	}

	// compiles static field load/store operations and static method calls
	private final VaDatatype compileStatic(int action, AstSpace parent,
			KorAstNode node, int result) throws CclibException,
			KorUtils.UnresolvedException {
		if(node instanceof AstTerm) {
			// start with the root
			AstSpace space = parent;
			AstSymbol symbol = (AstSymbol) node;
			while(true){
				// get the symbol's child
				final AstSymbol next = symbol.getChild();
				if(symbol instanceof AstTerm){
					final AstTerm subterm = (AstTerm) symbol;
					// get the child node
					final KorAstNode child = space.getSpaceEntry(subterm
							.getIdentifier());
					if(child == null)
						throw new KorUtils.UnresolvedException();
					if(child instanceof AstSpace){
						/*
						 * the symbol must have a child <= the last element of
						 * the symbol must not be a namespace
						 */
						if(next == null)
							throw new CclibException("Compile error: "
									+ subterm.getIdentifier()
									+ " is not a class but a namespace",
									unit, symbol.line());
						// continue with the child space
						space = (AstSpace) child;
						// continue with the child symbol
						symbol = symbol.getChild();
					}else if(child instanceof AstClass){
						// convert child to as ast class
						final AstClass cls = (AstClass) child;
						// get the compiler for the class
						final KorClsCompiler compiler = imageCmp.lookupClass(cls);
						// create a class type for the class
						final VaDatatype.DtClass type = new VaDatatype.DtClass(compiler
								.getClsInfo());
						if(next == null
								&& action != ACTION_ALLOC_RESOLVE
								&& action != ACTION_ALLOC)
							throw new CclibException("Compile error: "
									+ "Cannot access class "
									+ subterm.getIdentifier()
									+ " directly", unit, symbol.line());
						// compile the static access
						return compileStaticSymbol(action, type, next, result);
					}else if(child instanceof AstGeneric){
						// next next simple must not be null
						if(next == null)
							throw new CclibException("Compile error: "
									+ subterm.getIdentifier()
									+ " is a generic class",
									unit, symbol.line());
						// the next symbol must be a generic instance
						if(next instanceof AstInstance == false)
							throw new CclibException("Compile error: "
									+ subterm.getIdentifier()
									+ " is a generic class",
									unit, symbol.line());
						// merge the generic instance
						final VaClsInfo inst = imageCmp.mergeGeneric(
								classCmp, (AstGeneric) child,
								(AstInstance) next).getClsInfo();
						// create a class type for the class
						final VaDatatype.DtClass type = new VaDatatype.DtClass(inst);
						// compile the static access
						return compileStaticSymbol(action, type, next
								.getChild(), result);
					}else{
						throw new CclibException("Internal error: Unknown ast node "
								+ child.getClass().getSimpleName(), unit, child
								.line());
					}
				}else{
					throw new CclibException("Internal error: Unknown symbol type "
							+ symbol.getClass().getSimpleName(), unit, symbol
							.line());
				}
			}
		}
		
		throw new CclibException("Compile error: Could not resolve the node "
				+ node.getClass().getSimpleName(), unit, node.line());
	}

	// compiles static symbols (e.g. array creation or class member access)
	private final VaDatatype compileStaticSymbol(int action, VaDatatype type,
			AstSymbol symbol, int result) throws CclibException {
		if(symbol instanceof AstArrayAccess){
			final AstArrayAccess access = (AstArrayAccess) symbol;
			// array access nodes must not contain sub symbols
			if(access.getChild() != null)
				throw new CclibException("Compile error: " +
						"Invalid sub symbol of array access node",
						unit, access.line());

			final VaDatatype arraytype = new VaDatatype.DtArray(type, 0);
			
			if(action == ACTION_ALLOC) {
				// load the array size
				boolean free = false;
				int sizereg = localLookup(access.getIndex());
				if(sizereg == REGISTER_NULL){
					sizereg = registerAlloc(new VaDatatype.DtPrimitive(
							VaDatatype.DtPrimitive.PRIM_UINT));
					load(access.getIndex(), sizereg);
					free = true;
				}
				
				putInst(new GnInst.InstCreateArray(arraytype, sizereg, result));
				
				// free allocated registers
				if(free)
					registerFree(sizereg);
			}else if(action != ACTION_ALLOC_RESOLVE){
				throw new CclibException("Compile error: Invalid action for "
						+ access.getClass().getSimpleName(), unit, access
						.line());
			}
			
			// return the array type
			return arraytype;
		}else if(symbol instanceof AstArray){
			final VaDatatype.DtArray subtype = new VaDatatype.DtArray(type, 0);
			return compileStaticSymbol(action, subtype, symbol.getChild(),
					result);
		}
		
		// compile class member access
		if(type instanceof VaDatatype.DtClass){
			final VaDatatype.DtClass clstype = (VaDatatype.DtClass) type;
			final VaClass cls = imageCmp.lookupClass(clstype.clsInfo)
					.getClassBody();
			return compileClassElement(action, cls, symbol, result);
		}else{
			throw new CclibException("Internal error: Unknown symbol "
					+ symbol.getClass().getSimpleName(), unit, symbol.line());
		}
	}

	// compiles member symbols (e.g. field access, array access)
	private final VaDatatype compileMemberSymbol(int action, VaDatatype type,
			int object, AstSymbol symbol, int result) throws CclibException {
		if(type instanceof VaDatatype.DtClass) {
			// convert the type to a class type
			final VaDatatype.DtClass clstype = (VaDatatype.DtClass) type;
			
			// get the class of the register
			final VaClass cls = imageCmp.lookupClass(clstype.clsInfo)
					.getClassBody();
			
			// compile the child element
			return compileObjectElement(action, cls, object, symbol, result);
		}else if(type instanceof VaDatatype.DtArray) {
			if(symbol instanceof AstArrayAccess == false)
				throw new CclibException("Illegal operation for array",
						unit, symbol.line());
			return compileArrayAccess(action, (VaDatatype.DtArray) type, object,
					(AstArrayAccess) symbol, result);
		}else if(type instanceof VaDatatype.DtDelegate) {
			if(symbol instanceof AstCall) {
				return compileCall(action, type, object, null, (AstCall)symbol, result);
			}else{
				throw new CclibException("Internal error: " +
						"Unknown symbol for delegate", unit, symbol.line());
			}
		}else{
			throw new CclibException("Internal error: Unknown type "
					+ type.getClass().getSimpleName() + " during compilation "
					+ "of expression " + symbolToString(symbol) + " (Action: "
					+ action + ")", unit, symbol.line());
		}
	}

	// compiles static field accesses & static method invokations
	private final VaDatatype compileClassMember(final int action,
			final VaClass cls, final KorAstNode node,
			final int result) throws CclibException,
			KorUtils.UnresolvedException {
		if(node instanceof AstTerm){
			final AstTerm term = (AstTerm) node;
			final String ident = term.getIdentifier();
			
			// lookup the method or field in the class "cls"
			// or one of its super classes
			VaClass clsbody = cls;
			while(clsbody != null) {
				final int fldcnt = clsbody.getFieldCnt();
				for(int i = 0; i < fldcnt; i++){
					final VaFldInfo fld = clsbody.getField(i);
					if(fld.ident.equals(ident) == false)
						continue;
					if((fld.flags0 & VaFldInfo.FLAG0_STATIC) == 0)
						continue;
					return compileField(action, fld, REGISTER_NULL,
							term.getChild(), result);
				}
			
				final int mthcnt = clsbody.getMethodCnt();
				for(int i = 0; i < mthcnt; i++){
					final VaMthInfo mth = clsbody.getMethod(i);
					if(!mth.ident.equals(ident))
						continue;
					if((mth.flags0 & VaMthInfo.FLAG0_STATIC) == 0)
						continue;
					return compileCall(action, new VaDatatype.DtClass(clsbody.clsInfo),
							REGISTER_NULL, ident, (AstCall) term.getChild(), result);
				}
				
				// continue with the super class
				clsbody = clsbody.parentInfo == null
						? null : imageCmp.lookupClass(clsbody.parentInfo).getClassBody();
			}
			
			throw new KorUtils.UnresolvedException();
		}else{
			throw new CclibException("Compile error: Invalid target for subsymbol",
					unit, node.line());
		}
	}

	private final VaDatatype compileClassElement(int action, VaClass cls,
			KorAstNode node, int result) throws CclibException {
		// compile object allocations
		if(action == ACTION_ALLOC || action == ACTION_ALLOC_RESOLVE){
			return compileAlloc(action, cls, node, result);
		}
		try{
			return compileClassMember(action, cls, node, result);
		}catch (KorUtils.UnresolvedException e){
			throw new CclibException("Compile error: Could not resolve node "
					+ symbolToString(node), unit, node.line());
		}
	}

	// compiles an array access
	private final VaDatatype compileArrayAccess(int action,
			VaDatatype.DtArray type, int object,
			AstArrayAccess node, int result) throws CclibException {
		if(node.getChild() != null
				&& (action == ACTION_LOAD
						|| action == ACTION_STORE
						|| action == ACTION_INVOKE
						|| action == ACTION_RESOLVE)) {
			if(action == ACTION_RESOLVE) {
				return compileMemberSymbol(ACTION_RESOLVE, type.basetype,
					REGISTER_NULL, node.getChild(), REGISTER_NULL);
			}
			
			// allocate a temporary register
			int tempreg = registerAlloc(type.basetype);
			
			// load the element index
			boolean free = false;
			int indexreg = localLookup(node.getIndex());
			if(indexreg == REGISTER_NULL){
				// allocate a new register
				indexreg = registerAlloc(new VaDatatype.DtPrimitive(
						VaDatatype.DtPrimitive.PRIM_UINT));
				
				// load the index expression
				load(node.getIndex(), indexreg);
				
				// set the free flag
				free = true;
			}
			
			// FIXME: check register type!!!
			// FIXME: improved error checking (VaDatatype.DtArray etc)
			putInst(new GnInst.InstArrAccess
					(GnInst.InstArrAccess.OP_LOAD,
					object, indexreg, tempreg));
			
			// compile the sub symbols
			VaDatatype subtype = compileMemberSymbol(action, type.basetype, tempreg,
					node.getChild(), result);
			
			// free allocated registers
			registerFree(tempreg);
			if(free)
				registerFree(indexreg);
			
			return subtype;
		}
		
		if(action == ACTION_LOAD){
			// load the element index
			boolean free = false;
			int indexreg = localLookup(node.getIndex());
			if(indexreg == REGISTER_NULL){
				indexreg = registerAlloc(new VaDatatype.DtPrimitive(
						VaDatatype.DtPrimitive.PRIM_UINT));
				load(node.getIndex(), indexreg);
				free = true;
			}
			
			// FIXME: check register type!!!
			// FIXME: improved error checking (VaDatatype.DtArray etc)
			putInst(new GnInst.InstArrAccess
					(GnInst.InstArrAccess.OP_LOAD,
					object, indexreg, result));
			
			// free allocated registers
			if(free)
				registerFree(indexreg);
		}else if(action == ACTION_STORE) {
			// load the element index
			boolean free_index = false;
			int indexreg = localLookup(node.getIndex());
			if(indexreg == REGISTER_NULL){
				indexreg = registerAlloc(new VaDatatype.DtPrimitive(
						VaDatatype.DtPrimitive.PRIM_UINT));
				load(node.getIndex(), indexreg);
				free_index = true;
			}
			// FIXME: check register type!!!
			// FIXME: improved error checking (VaDatatype.DtArray etc)
			putInst(new GnInst.InstArrAccess
					(GnInst.InstArrAccess.OP_STORE,
					object, indexreg, result));
			
			// free allocated registers
			if(free_index)
				registerFree(indexreg);
		}else if(action != ACTION_RESOLVE) {
			throw new CclibException("Compile error: Invalid action for "
					+ node.getClass().getSimpleName(), unit, node.line());
		}
		// return the base type
		return type.basetype;
	}

	// compiles a method call
	private final VaDatatype compileCall(int action, VaDatatype objtype, int object,
			String ident, AstCall call, int result) throws CclibException {
		// get the argument count
		final int argcnt = call.parameterCount();
		
		// types of the arguments
		final VaDatatype[] argtypes = new VaDatatype[argcnt];
		
		// register of the argument
		final int[] argregs = new int[argcnt];
		
		// get the child symbol
		final AstSymbol child = call.getChild();
		
		// resolve the argument types
		for(int i = 0; i < argcnt; i++){
			final KorAstNode node = call.parameterAt(i);
			
			if(action == ACTION_DELEGATE
					|| action == ACTION_DELEGATE_RESOLVE
					|| action == ACTION_XNAT_MTHADDR) {
				argtypes[i] = classCmp.typeResolve(node);
			}else {
				argtypes[i] = resolve(node);
			}
		}

		// process delegate calls
		if(objtype instanceof VaDatatype.DtDelegate) {
			final VaDatatype.DtDelegate dlgtype	= (VaDatatype.DtDelegate)objtype;
			
			// process actions that have to do a method invokation
			if(action == ACTION_LOAD
					|| action == ACTION_STORE
					|| action == ACTION_INVOKE) {
				// load the arguments into registers
				for(int i = 0; i < argcnt; i++)
					argregs[i] = operandCast(dlgtype.getArgument(i), call.parameterAt(i));
				
				// allocate a register for the result
				final int dest;
				if(child != null) {
					dest = registerAlloc(dlgtype.returnType);
				}else{
					dest = result;
				}
				
				putInst(new GnInst.InstInvoke(null, object, dest, false, argregs));

				// process sub symbols
				if(child != null) {
					if(action == ACTION_RESOLVE)
						return compileMemberSymbol(ACTION_RESOLVE, dlgtype.returnType,
								REGISTER_NULL, child, REGISTER_NULL);
					final VaDatatype subtype = compileMemberSymbol(action,
							dlgtype.returnType, dest, child, result);
					
					// free the dest register (its a temp register)
					registerFree(dest);
					
					return subtype;
				}
			}
			
			// process sub symbols
			if(child != null)
					return compileMemberSymbol(action, dlgtype.returnType,
							REGISTER_NULL, child, result);
			
			return dlgtype.returnType;
		}
		
		final VaDatatype.DtClass clstype = (VaDatatype.DtClass)objtype;
		final VaClass cls = imageCmp.lookupClass(clstype.clsInfo).getClassBody();
		
		// select a suitable method
		final VaMthInfo mth = findSuitableMth(cls, ident, argtypes, call);
		if(mth == null) {
			final StringBuffer buffer = new StringBuffer();
			buffer.append(ident);
			buffer.append("(");
			for(int i = 0; i < argtypes.length; i++) {
				buffer.append(argtypes[i].toString());
				if(i != argtypes.length - 1)
					buffer.append(", ");
			}
			buffer.append(")");
			throw new CclibException("Compile error: " +
					"Could not find a suitable method "
					+ buffer.toString(), unit, call.line());
		}
		
		// load the arguments
		if(action == ACTION_LOAD
				|| action == ACTION_STORE
				|| action == ACTION_INVOKE)
			for(int i = 0; i < argcnt; i++) {
				argregs[i] = operandCast(mth.getArgument(i), call.parameterAt(i));
			}
		
		// process delegateof and xnat:mthaddr directives
		if(action == ACTION_DELEGATE_RESOLVE) {
			if(child != null)
				throw new CclibException("Internal error: Illegal method",
						unit, call.line());
			
			return new VaDatatype.DtDelegate(mth.returnType, argtypes);
		}else if(action == ACTION_DELEGATE) {
			if(child != null)
				throw new CclibException("Internal error: Illegal method",
						unit, call.line());
			if((mth.flags0 & VaMthInfo.FLAG0_STATIC) != 0) {
				putInst(new GnInst.InstDlgConstruct(mth, REGISTER_NULL, result));
			}else{
				putInst(new GnInst.InstDlgConstruct(mth, object, result));
			}
			return null;
		}else if(action == ACTION_XNAT_MTHADDR) {
			if(child != null)
				throw new CclibException("Internal error: Illegal method",
						unit, call.line());
			putInst(new GnInst.InstXNatMthAddress(result, mth));
			return null;
		}
		
		// determine the destination register
		final int dest;
		if(child != null) {
			dest = registerAlloc(mth.returnType);
		}else{
			dest = result;
		}
		
		if(action != ACTION_RESOLVE) {
			if(object == 0 || (mth.flags0 & VaMthInfo.FLAG0_FINAL) != 0) {
				putInst(new GnInst.InstInvoke(mth, object, dest, false, argregs));
			}else{
				putInst(new GnInst.InstInvoke(mth, object, dest, true, argregs));
			}
		
			// free the argument registers
			for(int i = 0; i < argcnt; i++)
				registerFree(argregs[i]);
		}
		
		// compile sub symbols
		if(child != null) {
			if(action == ACTION_RESOLVE)
				return compileMemberSymbol(ACTION_RESOLVE, mth.returnType,
						REGISTER_NULL, child, REGISTER_NULL);
			final VaDatatype subtype = compileMemberSymbol(action, mth.returnType,
					dest, child, result);
			
			// free the dest register (its a temp register)
			registerFree(dest);
			
			return subtype;
		}
		
		return mth.returnType;
	}

	private final VaDatatype compileAlloc(int action, VaClass cls, KorAstNode node,
			int result) throws CclibException {
		// FIXME: check whether object must be constructed
		if(action == ACTION_ALLOC){
			// generate an object creation inst
			putInst(new GnInst.InstCreateObject
					(new VaDatatype.DtClass(cls.clsInfo), result));

			// call the constructor
			if(node != null)
				compileCall(ACTION_INVOKE, new VaDatatype.DtClass(cls.clsInfo),
						result, "constructor",(AstCall) node, REGISTER_NULL);
		}else if(action != ACTION_ALLOC_RESOLVE){
			throw new CclibException("Compile error: " +
					"Invalid action for object construction",
					unit, node.line());
		}
		return new VaDatatype.DtClass(cls.clsInfo);
	}

	private final VaDatatype compileField(int action, VaFldInfo fld,
			int object, AstSymbol symbol, int result) throws CclibException {
		final VaDatatype fldtype = fld.datatype;
		
		if(object == REGISTER_NULL) {
			// compile sub symbols
			if(symbol != null
					&& (action == ACTION_STORE
							|| action == ACTION_LOAD
							|| action == ACTION_INVOKE
							|| action == ACTION_RESOLVE
							|| action == ACTION_DELEGATE 
							|| action == ACTION_DELEGATE_RESOLVE)){
				if(action == ACTION_RESOLVE
						|| action == ACTION_DELEGATE_RESOLVE)
					return compileMemberSymbol(action, fldtype, REGISTER_NULL,
							symbol, REGISTER_NULL);
				
				final int register = registerAlloc(fldtype);
				putInst(new GnInst.InstFldAccess(GnInst.InstFldAccess.OP_LOAD,
						fld, REGISTER_NULL, register));
				compileMemberSymbol(action, fldtype, register, symbol, result);
				registerFree(register);
				return null;
			}
			
			// compile static field accesses
			if(action == ACTION_STORE){
				// XXX: mechanics
				int sourcereg = result;
				if(!suitableForOperation(registerType(result), fldtype)) {
					sourcereg = registerAlloc(fldtype);
					putInst(new GnInst.InstCast(fldtype, sourcereg, result));
				}
				
				putInst(new GnInst.InstFldAccess(GnInst.InstFldAccess.OP_STORE,
						fld, REGISTER_NULL, sourcereg));
				if(sourcereg != result)
					registerFree(sourcereg);
			}else if(action == ACTION_LOAD){
				if(fld.literal != null) {
					putInst(new GnInst.InstLoad(result, fld.literal));
				}else{
					putInst(new GnInst.InstFldAccess(GnInst.InstFldAccess.OP_LOAD,
							fld, 0, result));
				}
			}else if(action != ACTION_RESOLVE)
				throw new CclibException("Compile error: " +
						"Invalid action for static field access", unit, -1);
			
			// return the return type of the field
			return fld.datatype;
		}else{
			// compile atomic compare-and-swap operations
			if(symbol instanceof AstXAtomCas) {
				final AstXAtomCas operation = (AstXAtomCas)symbol;
				
				if(action == ACTION_LOAD
						|| action == ACTION_INVOKE) {
					final int sourcereg = operandCast(fld.datatype,
							operation.source);
					final int valuereg = operandCast(fld.datatype,
							operation.value);
					putInst(new GnInst.InstXAtomCas(object, fld,
							sourcereg, valuereg, result));
					registerFree(sourcereg);
					registerFree(valuereg);
				}else if(action != ACTION_RESOLVE) {
					throw new CclibException("Compile error: " +
							"Invalid action for _atom:cas", unit, -1);
				}
				
				return new VaDatatype.DtPrimitive
						(VaDatatype.DtPrimitive.PRIM_BOOLEAN);
			}
			
			// compile sub symbols
			if(symbol != null
					&& (action == ACTION_STORE
							|| action == ACTION_LOAD
							|| action == ACTION_INVOKE
							|| action == ACTION_RESOLVE
							|| action == ACTION_DELEGATE
							|| action == ACTION_DELEGATE_RESOLVE)) {
				if(action == ACTION_RESOLVE
						|| action == ACTION_DELEGATE_RESOLVE)
					return compileMemberSymbol(action, fldtype,
							REGISTER_NULL, symbol, REGISTER_NULL);
				
				final int register = registerAlloc(fldtype);
				putInst(new GnInst.InstFldAccess(GnInst.InstFldAccess.OP_LOAD,
						fld, object, register));
				compileMemberSymbol(action, fldtype, register, symbol, result);
				registerFree(register);
				return null;
			}
			
			// compile object field accesses
			if(action == ACTION_STORE){
				// XXX: mechanics
				int sourcereg = result;
				if(!suitableForOperation(registerType(result), fldtype)) {
					sourcereg = registerAlloc(fldtype);
					putInst(new GnInst.InstCast(fldtype, sourcereg, result));
				}
				
				putInst(new GnInst.InstFldAccess(GnInst.InstFldAccess.OP_STORE,
						fld, object, sourcereg));
				if(sourcereg != result)
					registerFree(sourcereg);
			}else if(action == ACTION_LOAD){
				putInst(new GnInst.InstFldAccess(GnInst.InstFldAccess.OP_LOAD,
						fld, object, result));
			}else if(action != ACTION_RESOLVE){
				throw new CclibException("Compile error: " +
						"Invalid action for non-static field access", unit, -1);
			}
			
			return fld.datatype;
		}
	}

	// FIXME: enable type checking !!!!
	private final void compileJumpIfTrue(KorAstNode exp, int label)
			throws CclibException {
		if(exp instanceof AstOperation){
			// get the operation node
			AstOperation op = (AstOperation) exp;
			// get the operand nodes
			KorAstNode operand1 = op.getOperand1();
			KorAstNode operand2 = op.getOperand2();
			// process logical and/or
			switch(op.operator()) {
			case AstOperation.OP_LOGICAL_OR:
				compileJumpIfTrue(operand1, label);
				compileJumpIfTrue(operand2, label);
				return;
			case AstOperation.OP_LOGICAL_AND:
				currentLabel++;
				int templabel = currentLabel;
				final GnFrame tempframe = createFrame();
				
				compileJumpIfFalse(operand1, templabel);
				compileJumpIfFalse(operand2, templabel);
				putInst(new GnInst.InstJump(label, 0));
				currentFrame = tempframe;
				return;
			}
			
			// load the first operand into a register
			final VaDatatype lefttype = resolve(operand1);
			final int tempreg1 = registerAlloc(lefttype);
			load(operand1, tempreg1);
			
			// process instanceof
			if(op.operator() == AstOperation.OP_SPECIAL_INSTANCEOF){
				VaDatatype type = classCmp.typeResolve(operand2);
				
				// do the actual jump
				final int temp = registerAlloc(new VaDatatype.DtPrimitive
						(VaDatatype.DtPrimitive.PRIM_BOOLEAN));
				putInst(new GnInst.InstInstanceof(type, tempreg1, temp));
				putInst(new GnInst.InstJump(label, temp));
				
				// free the registers
				registerFree(temp);
				registerFree(tempreg1);
				return;
			}
			
			// load the second operand into a register
			final VaDatatype righttype = resolve(operand2);
			final int tempreg2 = registerAlloc(righttype);
			load(operand2, tempreg2);
			
			// verify the operand register types
			int reg1 = tempreg1;
			int reg2 = tempreg2;
			if(!suitableForOperation(lefttype, righttype)) {
				final VaDatatype optype = suitableTypeForOperation(lefttype,
						righttype, exp.line());
				if(typesCompatible(lefttype, optype) != CO_EQUAL) {
					reg1 = registerAlloc(optype);
					putInst(new GnInst.InstCast(optype, reg1, tempreg1));
					registerFree(tempreg1);
				}
				if(typesCompatible(righttype, optype) != CO_EQUAL) {
					reg2 = registerAlloc(optype);
					putInst(new GnInst.InstCast(optype, reg2, tempreg2));
					registerFree(tempreg2);
				}
			}
			
			final int temp = registerAlloc(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_BOOLEAN));
			
			// process usual operators
			switch(op.operator()) {
			case AstOperation.OP_COMPARE_GREATER:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_ABOVE,
						temp, reg1, reg2));
				break;
			case AstOperation.OP_COMPARE_LESS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_BELOW,
						temp, reg1, reg2));
				break;
			case AstOperation.OP_COMPARE_EQUALS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_EQUALS,
						temp, reg1, reg2));
				break;
			case AstOperation.OP_COMPARE_NOT_GREATER:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_ABOVE,
						temp, reg1, reg2));
				putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
						temp, temp));
				break;
			case AstOperation.OP_COMPARE_NOT_LESS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_BELOW,
						temp, reg1, reg2));
				putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
						temp, temp));
				break;
			case AstOperation.OP_COMPARE_NOT_EQUALS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_EQUALS,
						temp, reg1, reg2));
				putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
						temp, temp));
				break;
			default:
				// invalid operator
				throw new CclibException("Compile error: " +
						"Illegal boolean operator", unit, op.line());
			}
			
			putInst(new GnInst.InstJump(label, temp));
			
			// free the registers
			registerFree(reg1);
			registerFree(reg2);
		}else{
			// process boolean operands
			int reg = localLookup(exp);
			if(reg == 0){
				final VaDatatype type = resolve(exp);
				reg = registerAlloc(type);
				load(exp, reg);
				putInst(new GnInst.InstJump(label, reg));
				registerFree(reg);
			}else{
				putInst(new GnInst.InstJump(label, reg));
			}
		}
	}

	private final void compileJumpIfFalse(KorAstNode exp, int label)
			throws CclibException {
		if(exp instanceof AstOperation){
			// get the operation node
			final AstOperation op = (AstOperation) exp;
			// get the operand nodes
			final KorAstNode operand1 = op.getOperand1();
			final KorAstNode operand2 = op.getOperand2();
			
			// process logical and/or
			switch(op.operator()) {
			case AstOperation.OP_LOGICAL_AND:
				compileJumpIfFalse(operand1, label);
				compileJumpIfFalse(operand2, label);
				// break
				return;
			case AstOperation.OP_LOGICAL_OR:
				// create a temp label
				currentLabel++;
				final int templabel = currentLabel;
				final GnFrame tempframe = createFrame();
				
				compileJumpIfTrue(operand1, templabel);
				compileJumpIfTrue(operand2, templabel);
				putInst(new GnInst.InstJump(label, 0));
				currentFrame = tempframe;
				return;
			}
			
			// load the first operand into a register
			final VaDatatype lefttype = resolve(operand1);
			final int tempreg1 = registerAlloc(lefttype);
			load(operand1, tempreg1);
			
			// process instanceof
			if(op.operator() == AstOperation.OP_SPECIAL_INSTANCEOF){
				final VaDatatype type = classCmp.typeResolve(operand2);
				
				// do the actual jump
				final int temp = registerAlloc(new VaDatatype.DtPrimitive
						(VaDatatype.DtPrimitive.PRIM_BOOLEAN));
				putInst(new GnInst.InstInstanceof(type, tempreg1, temp));
				putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
						temp, temp));
				putInst(new GnInst.InstJump(label, temp));
				
				// free the registers
				registerFree(temp);
				registerFree(tempreg1);
				// break
				return;
			}
			
			// load the second operand into a register
			final VaDatatype righttype = resolve(operand2);
			final int tempreg2 = registerAlloc(righttype);
			load(operand2, tempreg2);
			
			// verify the operand register types
			int reg1 = tempreg1;
			int reg2 = tempreg2;
			if(!suitableForOperation(lefttype, righttype)) {
				final VaDatatype optype = suitableTypeForOperation(lefttype,
						righttype, exp.line());
				if(typesCompatible(lefttype, optype) != CO_EQUAL) {
					reg1 = registerAlloc(optype);
					putInst(new GnInst.InstCast(optype, reg1, tempreg1));
					registerFree(tempreg1);
				}
				if(typesCompatible(righttype, optype) != CO_EQUAL) {
					reg2 = registerAlloc(optype);
					putInst(new GnInst.InstCast(optype, reg2, tempreg2));
					registerFree(tempreg2);
				}
			}
			
			final int temp = registerAlloc(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_BOOLEAN));
			
			switch(op.operator()) {
			case AstOperation.OP_COMPARE_GREATER:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_ABOVE,
						temp, reg1, reg2));
				putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
						temp, temp));
				break;
			case AstOperation.OP_COMPARE_LESS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_BELOW,
						temp, reg1, reg2));
				putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
						temp, temp));
				break;
			case AstOperation.OP_COMPARE_EQUALS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_EQUALS,
						temp, reg1, reg2));
				putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
						temp, temp));
				break;
			case AstOperation.OP_COMPARE_NOT_GREATER:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_ABOVE,
						temp, reg1, reg2));
				break;
			case AstOperation.OP_COMPARE_NOT_LESS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_BELOW,
						temp, reg1, reg2));
				break;
			case AstOperation.OP_COMPARE_NOT_EQUALS:
				putInst(new GnInst.InstBinOperation(GnInst.InstBinOperation.OP_SETIF_EQUALS,
						temp, reg1, reg2));
				break;
			default:
				throw new CclibException("Compile error: " +
						"Invalid boolean operator", unit, op.line());
			}
			
			// do the actual jump
			putInst(new GnInst.InstJump(label, temp));
			
			// free the registers
			registerFree(temp);
			registerFree(reg1);
			registerFree(reg2);
		}else{
			// process boolean operands
			int register = localLookup(exp);
			boolean free = false;
			if(register == 0){
				final VaDatatype type = resolve(exp);
				register = registerAlloc(type);
				load(exp, register);
				free = true;
			}
			
			final int temp = registerAlloc(new VaDatatype.DtPrimitive
					(VaDatatype.DtPrimitive.PRIM_BOOLEAN));
			putInst(new GnInst.InstUnOperation(GnInst.InstUnOperation.OP_ARITH_NOT,
					temp, register));
			putInst(new GnInst.InstJump(label, temp));
			registerFree(temp);
			
			if(free)
				registerFree(register);
		}
	}
	
	private int operandLoad(final KorAstNode node) throws CclibException {
		final int register = registerAlloc(resolve(node));
		load(node, register);
		return register;
	}
	
	private int operandCast(final VaDatatype resulttype,
			final KorAstNode expression) throws CclibException {
		final VaDatatype exptype = resolve(expression);
		
		final int expregister = operandLoad(expression);
		if(suitableForOperation(exptype, resulttype))
			return expregister;
		
		// cast it to the specified type
		final int resregister = registerAlloc(resulttype);
		putInst(new GnInst.InstCast(resulttype, resregister, expregister));
		registerFree(expregister);
		return resregister;
	}
	
	private boolean suitableForOperation(final VaDatatype left,
			final VaDatatype right) {
		if(left instanceof VaDatatype.DtClass
				&& right instanceof VaDatatype.DtClass) {
			return true;
		}else if(left instanceof VaDatatype.DtArray
				&& right instanceof VaDatatype.DtArray) {
			return true;
		}else if(left instanceof VaDatatype.DtPrimitive
				&& right instanceof VaDatatype.DtPrimitive) {
			VaDatatype.DtPrimitive leftprim = (VaDatatype.DtPrimitive)left;
			VaDatatype.DtPrimitive rightprim = (VaDatatype.DtPrimitive)right;
			return leftprim.identity == rightprim.identity;
		}else if(left instanceof VaDatatype.DtDelegate
				&& right instanceof VaDatatype.DtDelegate) {
			// FIXME:
			return true;
		}
		return false;
	}
	
	private VaDatatype suitableTypeForOperation(final VaDatatype left,
			final VaDatatype right, int line) throws CclibException {
		if(left instanceof VaDatatype.DtPrimitive) {
			VaDatatype.DtPrimitive leftprim = (VaDatatype.DtPrimitive)left;
			if(leftprim.identity == VaDatatype.DtPrimitive.PRIM_NULL)
				return right;
		}
		if(right instanceof VaDatatype.DtPrimitive) {
			VaDatatype.DtPrimitive rightprim = (VaDatatype.DtPrimitive)right;
			if(rightprim.identity == VaDatatype.DtPrimitive.PRIM_NULL)
				return left;
		}
		if(right instanceof VaDatatype.DtPrimitive == false
				|| left instanceof VaDatatype.DtPrimitive == false)
			throw new CclibException("Compile error: Incompatible types",
					unit, line);
		VaDatatype.DtPrimitive leftprim = (VaDatatype.DtPrimitive)left;
		VaDatatype.DtPrimitive rightprim = (VaDatatype.DtPrimitive)right;
		
		if(primitiveFormat(leftprim.identity) != primitiveFormat(rightprim.identity)) {
			throw new CclibException("Compile error: Incompatible primitive types",
					unit, line);
		}
		
		final int index1 = primitiveIndex(leftprim.identity);
		final int index2 = primitiveIndex(rightprim.identity);
		
		return (index1 > index2) ? leftprim : rightprim;
	}
	
	private String symbolToString(final KorAstNode node)
			throws CclibException {
		return symbolToString(node, true);
	}
	private String symbolToString(final KorAstNode node, boolean firstone)
			throws CclibException {
		final StringBuffer buffer = new StringBuffer();
		
		if(node instanceof AstTerm) {
			final AstTerm object = (AstTerm)node;

			if(firstone == false)
				buffer.append(".");
			buffer.append(object.getIdentifier());
			
			// append child nodes
			if(object.getChild() != null)
				buffer.append(symbolToString(object.getChild(), false));
		}else if(node instanceof AstCall) {
			final AstCall object = (AstCall)node;
			
			buffer.append("(");
			final VaDatatype[] args = resolveArguments(object);
			for(int i = 0; i < args.length; i++) {
				buffer.append(args[i].toString());
				if(i != args.length - 1)
					buffer.append(", ");
			}
			buffer.append(")");
			
			// append child nodes
			if(object.getChild() != null)
				buffer.append(symbolToString(object.getChild(), false));
		}else if(node instanceof AstArrayAccess) {
			final AstArrayAccess object = (AstArrayAccess)node;
			
			buffer.append("[...]");
			
			// append child nodes
			if(object.getChild() != null)
				buffer.append(symbolToString(object.getChild(), false));
		}else{
			return "<Illegal node for symbolToString()>";
		}
		
		return buffer.toString();
	}
}
